package com.lqian.aspect;

import com.lqian.annotation.RetryOnFailure;
import com.lqian.exception.BadRequestException;
import com.lqian.exception.RetryOnFailureException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @author：Melody_LQ
 * @time：2022/8/19 5:26 下午
 */
@Aspect
@Component
@Slf4j // 切面
@Order(1) // 最优先
/**@Order
 *  spring的事务和aop执行的先后问题
 *  定义重试切面方法，是为了防止发生乐观锁异常时，在一个全新的事务里面提交上一次的操作，直到达到重试次数为止。
 *  因此切面实现org.springframework.core.Ordered 接口，这样我们就可以把切面的优先级设定为高于事务通知
 *  @Order注解和Ordered接口不决定Bean的加载顺序和实例化顺序，只决定bean的执行顺序。
 *  getOrder()值越小，优先级越高。
@Order
1.spring4.2可以用@Order控制配置类的加载顺序
2.常用于aop优先事务执行
3.@Order不标注数字，默认最低优先级，即int的最大值

*/
public class RetryOnFailureAspect {
    private int maxRetries;
    public void setMaxRetries(int maxRetries) {
        this.maxRetries = maxRetries;
    }


    //切点
    @Pointcut("@annotation(com.lqian.annotation.RetryOnFailure)")
    public void retryOnOptFailure(){

    }

    @Around("retryOnOptFailure()")
    public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
        // 获取拦截的方法名
        MethodSignature msig = (MethodSignature) pjp.getSignature();
        // 返回被织入增加处理目标对象
        Object target = pjp.getTarget();
        // 为了获取注解信息
        Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
        // 获取注解信息
        RetryOnFailure annotation = currentMethod.getAnnotation(RetryOnFailure.class);
        // 设置重试次数
        this.setMaxRetries(annotation.tryTimes());
        // 重试次数
        int numAttempts = 0;
        do {
            numAttempts++;
            try {
                // 再次执行业务代码
   // =============================Before=============================
                return pjp.proceed();

            }
    // =============================After=============================
            catch (RetryOnFailureException ex) {
                if (numAttempts > maxRetries) {
                    // 如果大于 默认的重试机制 次数，我们这回就真正的抛出去了
                    throw new BadRequestException("服务内部错误-重试结束");
                }else{
                    // 如果 没达到最大的重试次数，将再次执行
                    log.info("^0^ === 正在重试====="+numAttempts+"次");
                }
            }
        } while (numAttempts <= this.maxRetries);

        return null;
    }


}
